/*
 * Google Maps Api jQuery Plugin
 * Berlin, Germany
 *
 * Matthias Kallenbach
 * seekwhencer.de
 * matthias.kallenbach@gmail.com
 */

(function($) {

    var _ns = 'googleMapsTools';

    var version = '0.1.1'; // Version 

    // default settings and options structure with content
    var defaults = {
        
        params : false,
        
        data : false, // json, the map data
        
        name : 'GMap',
        
        onInit : function() {
        }, // callback

        onDestroy : function() {
        }, // callback

        onSetOpt : function() {
        }, // callback

        // relative or absolute path of marker images
        imagePath : 'images/',

        // global nmarker icon
        markerIcon : 'marker_plus.png',

        // map specifications
        startPoint : {
            lat : 50.666872, // float
            lng : 10.19165 // float
        },
        startZoom : 5, // int
        maxZoom : 8, // int
        minZoom : 2, // int

        // google maps "style wizard" output
        mapStyles : [{
            "featureType" : "water",
            "stylers" : [{
                "color" : "#b3d0de"
            }]
        }, {
            "featureType" : "landscape",
            "stylers" : [{
                "color" : "#ffffff"
            }]
        }, {
            "featureType" : "road",
            "stylers" : [{
                "visibility" : "off"
            }]
        }, {
            "featureType" : "transit",
            "stylers" : [{
                "visibility" : "off"
            }]
        }, {
            "featureType" : "landscape.natural.terrain",
            "elementType" : "geometry",
            "stylers" : [{
                "visibility" : "off"
            }]
        }, {
            "featureType" : "administrative.province",
            "stylers" : [{
                "visibility" : "off"
            }]
        }, {
            "featureType" : "poi",
            "stylers" : [{
                "visibility" : "off"
            }]
        }, { }],

        centerOpenInfoboxOnWindowResize : true,
        disableCustomControls : false,
        disableDefaultUI : true,
        streetViewControl : false,
        mapTypeControl : false,
        mapTypeId : google.maps.MapTypeId.HYBRID,
        scrollWheel : false,

        // info box specifications
        infoBox : {
            offsetX : 10,
            offsetY : -78,
            alignBottom : true,
            disableAutoPan : false,
            zIndex : 1000,
            boxClass : 'infobox',
            boxClickClass : false,
            boxHoverClass : false,
            closeBoxMargin : 0,
            closeIcon : 'infobox-close-icon.png',
            isHidden : false,
            pane : 'floatPane', // or mapPane
            enableEventPropagation : false,
            openOn : 'hover', // or 'click'
            click : false,
            hover : false,
            closeOnClickOutside : false,
            centerMapOnShow : false,
            content : false, // html data - if data, the template : {} and the content build will be ignored @TODO
            templates : {
                triangle : '<div class="triangle"></div>',
                heading : '<h2>%%name%%</h2>',
                text : '<div class="text">%%text%%</div>',
                row : '<a href="%%src%%" %%external%% class="map-popover-link">%%text%%</a>',
                footer : '',
                xhrBody : '%%content%%'
            }
        },       

        templateMapControls : {
            body : '<div class="custom-controls">%%content%%</div>',
            move : '<div class="move"><a href="javascript:;" class="toleft"></a><a href="javascript:;" class="toright"></a><a href="javascript:;" class="toup"></a><a href="javascript:;" class="todown"></a></div>',
            zoom : '<div class="zoom"><a href="javascript:;" class="zoom-in"></a><a href="javascript:;" class="zoom-out"></a></div>',
            type : ''
        }
    };



    // build
    $.fn[_ns] = function(args) {
        var target = this;
        var $target = $(this);
        var map = false;
        var markers = new Array();
        var infoBoxes = new Array();
        var options = {};
        
        params = args;
        
        if ($.type(options) == 'string') {
            // ...
        }

        if ($.type(options) !== 'object') {
            var options = {};
        }

        $.extend(true, options, defaults, args);

        // except the options merge, map styles will be completely overwritten
        if ($.type(args.mapStyles) == 'array') {
            options.mapStyles = args.mapStyles;
        }

        // init
        function run(args) {


            //var options = $.extend( true, defaults, args);

            target.each(function() {
               
                assumeSettings();

                var that = $(this);
                              
                map = createMap(that[0]);

                if (options.disableCustomControls === false) {
                    addCustomControls();
                }
                
                // adds the marker
                if ($.type(options.places) == 'array') {

                    var index = 0;

                    $(options.places).each(function() {
                        var place   = this;
                        var marker  = createMarker(place,index);
                        var infobox = createInfobox(marker,place);

                        markers.push(marker);
                        infoBoxes.push(infobox);

                        index++;
                    });
                }
            });

            //
            if (options.infoBox.closeOnClickOutside === true) {
                $(target).on('click', function() {
                    if ($(this).find('.' + options.infoBox.boxClass + ':visible').length > 0)
                        if ($(this).find('.custom-controls:hover').length == 0)
                            hideAllInfoBoxes();
                });
            }

            if (options.centerOpenInfoboxOnWindowResize === true) {
                $(window).resize(function() {
                    moveToMarker('active');
                });
            }
        }

        /**
         *
         *
         */

        // merge settings from map data json
        function assumeSettings() {
            $.extend(true, options, options.data.settings, {
                places : options.data.places
            });
        }

        // destroy
        function destroy() {
            $target.each(function() {
                $(this).html('');
                $(this).removeAttr('style');

                callAfter('onDestroy');
            });
        }

        // call after
        function callAfter(method) {
            if (options[method] !== undefined) {
                options[method].call(target);
            }
        }

        // call before
        function callbefore(method) {
            if (options[method] !== undefined) {
                options[method].call(target);
            }
        }

        // set options
        function setOpt(options) {
            options = $.extend(defaults, options);
            callAfter('onSetOpt');
        }

        // get option key
        function getOpt(key) {
            return options[key];
        }

        // is option key value
        function isOpt(key, value) {
            if (options[key] == value)
                return true;

            return false;
        }
        
        /**
         * creates a map by using merged options
         *  
         */
        function createMap(target){
            var latlng      = new google.maps.LatLng(parseFloat(options.startPoint.lat), parseFloat(options.startPoint.lng));
            var styledMap   = new google.maps.StyledMapType(options.mapStyles, {
                name : options.name
            });

            var mapOptions = {
                zoom                : parseInt(options.startZoom),
                center              : latlng,
                mapTypeId           : options.mapTypeId,
                mapTypeControl      : options.mapTypeControl,
                disableDefaultUI    : options.disableDefaultUI,
                navigationControl   : true,
                scrollwheel         : options.scrollWheel,
                streetViewControl   : options.streetViewControl,
                mapTypeControlOptions : {
                    mapTypeIds : [options.mapTypeId, 'map_style'],
                },
                zoomControlOptions  : {
                    style : google.maps.ZoomControlStyle.SMALL
                },

            };

            map = new google.maps.Map(target, mapOptions);

            map.mapTypes.set('map_style', styledMap);
            map.setMapTypeId('map_style');
            
            return map;
        }
        
        /**
         * creating a marker 
         */
        function createMarker(place,index){
            
            //use place' marker icon or default
            if (place.marker_icon) {
                var markerIcon = place.marker_icon;
            } else {
                var markerIcon = options.markerIcon;
            }

            // marker options
            var markerOptions = {
                position    : new google.maps.LatLng(parseFloat(place.coordinates.lat), parseFloat(place.coordinates.lng)),
                map         : map,
                title       : place.name,
                icon        : options.imagePath + '' + markerIcon,
            };
            
            // create the marker
            var marker = new google.maps.Marker(markerOptions);
            
            // 
            $.extend(true, marker, {
                'index' : index
            });
            
            return marker;
        }
        
        /**
         * creating a infobox
         *  
         */
        function createInfobox(marker,place){
            
            var infoboxOptions = {
                index                   : marker.index,
                disableAutoPan          : options.infoBox.disableAutoPan,
                alignBottom             : options.alignBottom,
                pixelOffset             : new google.maps.Size(options.infoBox.offsetX, options.infoBox.offsetY),
                zIndex                  : options.infoBox.zIndex,
                boxClass                : options.infoBox.boxClass,
                closeBoxMargin          : options.infoBox.closeBoxMargin + 'px',
                closeBoxURL             : options.imagePath + '' + options.infoBox.closeIcon,
                infoBoxClearance        : new google.maps.Size(1, 1),
                isHidden                : options.infoBox.isHidden,
                pane                    : options.infoBox.pane,
                enableEventPropagation  : options.infoBox.enableEventPropagation
            
            };
            
            // create the infobox
            var infobox = new InfoBox(infoboxOptions);
            
            $.extend(true, infobox, {
                markerId    : marker.__gm_id,
                markerIndex : marker.index
            });
            
            /** 
             * Opens different Infoboxes by event: click or hover (mouseover)
             */
            if ( $.type(options.infoBox.click)=='string' ||  $.type(options.infoBox.hover)=='string') {
                
                // click
                if ( $.type(options.infoBox.click)=='string' ) {
                   addInfoboxToMarker(marker,infobox,place,'click');
                }
                
                // hover
                if ( $.type(options.infoBox.hover)=='string' ) {
                    addInfoboxToMarker(marker,infobox,place,'mouseover');
                }
            }
            
            /* open ONE infobox by using "options.infoBox.openOn" and by defining
            *  NO different infoboxes while leaving "options.infoBox.click" or "options.infoBox.hover" by default: false
            *  and using the single infobox markup in mapdata
            */ 
            if ( ( options.infoBox.click==false && options.infoBox.hover==false ) ) {                          
            
                //
                if (options.infoBox.openOn == 'hover') {
                    var openOn = 'mouseover';
                }
            
                //
                if (options.infoBox.openOn == 'click') {
                    var openOn = 'click';
                }
                                            
                //
                if (place.info_box.xhr_src) {
                    console.log('### FALLBACK, not defined: ' + openOn+ ' - '+place.info_box.xhr_src);
                    $.extend(true, marker, {
                        'xhr_src'       : place.info_box.xhr_src,
                        'xhr_method'    : place.info_box.xhr_method,
                        'xhr_options'   : place.info_box.xhr_options
                    });
                }
                                                   
                addInfoboxToMarker(marker,infobox,place,openOn);
            }
            
            return infobox;
        }
        //
        
        //
        function addInfoboxToMarker(marker,infobox,place,openOn){
            google.maps.event.addListener(marker, openOn, function() {
                hideAllInfoBoxes();
                
                if(openOn=='mouseover')
                    var openG = 'hover';
                
                if(openOn=='click')
                    var openG = 'click';
                
                
                if (options.infoBox.centerMapOnShow === true) {
                    moveTo(place.coordinates.lat, place.coordinates.lng);
                }
                
                var infobox_options = {};
                
                if (place.info_box.xhr_src) {
                    infobox_options = {
                        xhr_src     : place.info_box.xhr_src,
                        xhr_method  : place.info_box.xhr_method,
                        xhr_options : place.info_box.xhr_options
                    };
                }
                
                var place_infobox_options = place.info_box[options.infoBox[openG]];
                if(place_infobox_options){
                    if(place_infobox_options.xhr_src){
                        infobox_options = {
                            xhr_src     : place_infobox_options.xhr_src,
                            xhr_method  : place_infobox_options.xhr_method,
                            xhr_options : place_infobox_options.xhr_options
                        };
                    }
                }

                
                // adding class     
                if( $.type(options.infoBox.boxClickClass) == 'string' && openG == 'click'){
                    infobox.setOptions({
                        boxClass : options.infoBox.boxClickClass
                    });
                } 
                
                if( $.type(options.infoBox.boxHoverClass) == 'string'  && openG == 'hover'){
                    infobox.setOptions({
                        boxClass : options.infoBox.boxHoverClass
                    });
                }

                if (infobox_options.xhr_src) {
                    $.ajax({
                        url     : infobox_options.xhr_src,
                        type    : infobox_options.xhr_method,
                        data    : infobox_options.xhr_options
                    }).done(function(data) {
                        infobox.setContent(options.infoBox.templates.xhrBody.replace('%%content%%', data));
                        infobox.open(map, marker);
                    });
                } else {
                    infobox.setContent(drawInfoWindowContent(place,openG));
                    infobox.open(map, marker);
                }
                
            });
        }

        /**
         * format:
         *
         * name: "",
         * coordinates: {
         *      lat: '',
         *      lng: ''
         * },
         * info_box: {
         *      link: [{
         *              title:  "",
         *              text:   "",
         *              src:    ""
         *      }],
         *      text: ""
         *}
         *
         */
        function drawInfoWindowContent(place,than) {
            var html = '';
            var rows = new Array();
            
            if (place.name)
                var name = place.name;
                
            if (place.text)
                var text = place.text;
            
            if ($.type(place.info_box.link) == 'array') {
                rows = place.info_box.link;
            }
            
            if ($.type(place.info_box.title) == 'string') {
                rows = new Array(place.info_box);
            }
 
            
            if ( $.type(options.infoBox.click)=='string' ||  $.type(options.infoBox.hover)=='string') {
                
                var infobox_options = place.info_box[options.infoBox[than]];

                if(infobox_options){
                    if (infobox_options.text)
                        var text = infobox_options.text;
                    
                    if (infobox_options.name)
                        var name = infobox_options.name;
                    
                    if ($.type(infobox_options.link) == 'array') {
                        rows = infobox_options.link;
                    }
                    
                    if ($.type(infobox_options.title) == 'string') {
                        rows = new Array(infobox_options);
                    }
                }

            }
                        
            
            
            //
            html += options.infoBox.templates.triangle;
            html += '<div class="inner">';
            html += options.infoBox.templates.heading.replace('%%name%%', name);

            if (text)
                html += options.infoBox.templates.text.replace('%%text%%', text);

            for (var i = 0; i < rows.length; i++) {
                var l = rows[i];

                var external = '';
                // empty!
                if (l.external == true)
                    external = 'target="_blank";';

                html += options.infoBox.templates.row.replace('%%text%%', l.text).replace('%%src%%', l.src).replace('%%external%%', external);
            }
            html += '</div>';

            return html;
        }

        /**
         *
         *
         */
        function drawXHRWindowContent(content) {
            return options.infoBox.templates.xhrBody.replace('%%content%%', content);
        }

        /**
         *
         */
        function addCustomControls() {

            var html = '';
            html += options.templateMapControls.body.replace('%%content%%', options.templateMapControls.move + '' + options.templateMapControls.zoom + '' + options.templateMapControls.type);

            map.controls[google.maps.ControlPosition.LEFT_TOP].push($(html)[0]);

            google.maps.event.addListenerOnce(map, 'tilesloaded', function() {

                // animate the controls, funky stuff ;)
                $('.mapbox .custom-controls').animate({
                    opacity : 1,
                    marginTop : '100px'
                }, function() {
                    $('.mapbox .custom-controls .zoom').animate({
                        opacity : 1,
                        marginTop : '21px'
                    }, function() {

                        // zoom in
                        google.maps.event.addDomListener($('.custom-controls .zoom .zoom-in')[0], 'click', function() {
                            this.blur();
                            var zoom = map.getZoom();
                            if (zoom < options.maxZoom) {
                                map.setZoom(zoom + 1);
                            } else {
                                $('.custom-controls .zoom .zoom-in').animate({
                                    opacity : 0.3
                                }, 200, function() {
                                    $('.custom-controls .zoom .zoom-in').animate({
                                        opacity : 1
                                    }, 200);
                                });
                            }
                        });

                        // zoom out
                        google.maps.event.addDomListener($('.custom-controls .zoom .zoom-out')[0], 'click', function() {
                            this.blur();
                            var zoom = map.getZoom();
                            if (zoom > options.minZoom) {
                                map.setZoom(zoom - 1);
                            } else {
                                $(' .custom-controls .zoom .zoom-out').animate({
                                    opacity : 0.3
                                }, 200, function() {
                                    $('.custom-controls .zoom .zoom-out').animate({
                                        opacity : 1
                                    }, 200);
                                });
                            }
                        });

                        // move map up
                        google.maps.event.addDomListener($('.custom-controls .move .toup')[0], 'click', function() {
                            map.panBy(0, -100);
                            this.blur();
                        });

                        // move map up
                        google.maps.event.addDomListener($('.custom-controls .move .todown')[0], 'click', function() {
                            map.panBy(0, 100);
                            this.blur();
                        });

                        // move map left
                        google.maps.event.addDomListener($(' .custom-controls .move .toleft')[0], 'click', function() {
                            map.panBy(-100, 0);
                            this.blur();
                        });

                        // move map right
                        google.maps.event.addDomListener($(' .custom-controls .move .toright')[0], 'click', function() {
                            map.panBy(100, 0);
                            this.blur();
                        });
                    });
                });
            });
        }
        
        
        
        /*********************************************************
         *
         *  Functions from outside
         *  
         *********************************************************/

        /**
         *
         */
        function hideAllInfoBoxes() {
            for (var i = 0; i < infoBoxes.length; i++) {
                infoBoxes[i].close();
            }
        }

        /**
         *
         */
        function showAllInfoBoxes() {
            for (var i = 0; i < infoBoxes.length; i++) {
                infoBoxes[i].open(map, markers[i]);
            }
        }

        /**
         * @TODO
         *
         */
        function setStyle(args) {

        }

        /**
         * @TODO
         */
        function addMarker(marker) {

        }

        /**
         * centers the map by given lat & lng
         */
        function moveTo(lat, lng) {
            var latLng = new google.maps.LatLng(lat, lng);
            map.panTo(latLng);
        }

        function moveToMarker(args) {
            if (args == 'active') {
                var activeInfoBox = $target.find('.' + options.infoBox.boxClass + ':visible');
                var index = activeInfoBox.data('index');
                if (index === 0 || index > 0) {
                    moveTo(markers[index].position.lat(), markers[index].position.lng());
                }
            }

            if ($.type(args) == 'number') {
                moveTo(markers[args].position.lat(), markers[args].position.lng());
            }
        }

        /**
         * @TODO
         */
        function zoomTo(zoom) {

        }
        
        /**
         * 
         */
        function getOptions(){
            return options;
        }
        
        /**
         * 
         */
        function getDefaults(){
            return defaults;
        }
        
        /**
         * 
         */
        function getParams(){
            return params;
        }
        
        /**
         * 
         */
        function countMarkers(){
            return markers.length;
        }
        
        /**
         * 
         */
        function getMarkers(){
            return markers;
        }
        
        /**
         * 
         */
        function openMarker(markerIndex,openOn,center){
            hideAllInfoBoxes();
            google.maps.event.trigger(markers[parseInt(markerIndex)], openOn);
            
            if(center===true)
                moveToMarker(parseInt(markerIndex));
        }
        

        
        
        /**
         * 
         *
         */

        run(options); // for your life
 
        return {
            init            : run,
            destroy         : destroy,
            setOpt          : setOpt,
            setStyle        : setStyle,
            addMarker       : addMarker,
            moveTo          : moveTo,
            zoomTo          : zoomTo,
            hideAllInfoBoxes : hideAllInfoBoxes,
            showAllInfoBoxes : showAllInfoBoxes,
            getOptions      : getOptions,
            getDefaults     : getDefaults,
            getParams       : getParams,
            getMarkers      : getMarkers,
            countMarkers    : countMarkers,
            openMarker      : openMarker,
        };
    };
})(jQuery);
